/**
 * 
 */
package network;

import io.ImportGame;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

import game.Building;
import game.Game;
import game.Map;
import game.Player;
import game.Unit;
import game.UnitType;

/**
 * @author jb
 * 
 * Modified by Stefan
 *
 */
// TODO: GameClient class
public class GameServer extends GameNetwork {
	
	private ArrayList<Socket> clients;
	private ArrayList<BufferedReader> cmdIStreams;
	private ArrayList<DataOutputStream> cmdOStreams;
	
	public GameServer(String name, Map map, ArrayList<Player> players, ArrayList<Socket> clients) throws IOException
	{
		super(name, map, players);
		
		this.clients = clients;
		this.cmdIStreams = new ArrayList<BufferedReader>();
		this.cmdOStreams = new ArrayList<DataOutputStream>();
		
		for(Socket s : this.clients) 
		{
			this.cmdIStreams.add(new BufferedReader((new InputStreamReader(s.getInputStream()))));
			this.cmdOStreams.add(new DataOutputStream(s.getOutputStream()));
		}
	}
	
	//
	// Low level commands
	//

	public void transfertGameFile() {
		
	}
	
	public void readSocketClients()
	{
		for(BufferedReader r : this.cmdIStreams) {
			try {
				while(r.ready()) 
				{
					this.parseCommand(r.readLine());
				}
			} 
			catch (IOException e) 
			{
				e.printStackTrace();
			}
		}
	}
	
	public void sendToAll(String command)
	{
		try 
		{
			for(DataOutputStream d : this.cmdOStreams)
			{
				d.writeBytes(command + "\n");
			}
			
		} 
		catch (IOException e)
		{
			e.printStackTrace();
		}
		
	}
	
	public void setTile(boolean server, int x, int y, int tile)
	{
		if (server == true)
		{
			String command = "setTile\t"+ x + "\t" + y + "\t" + tile;
			
			this.sendToAll(command);
		}
		
		super.setTile(server, x, y, tile);
	}
	
	public void placeUnit(boolean server, Unit unit) 
	{
		if (server == true)
		{
			String command = "placeUnit\t" + unit;
			this.sendToAll(command);
		}
		super.placeUnit(server, unit);
	}
	
	public void removeUnit(boolean server, Unit unit) 
	{
		if (server == true)
		{
			String command = "removeUnit\t" + unit.getX() + "\t" + unit.getY();
			this.sendToAll(command);
		}
		super.removeUnit(server, unit);
	}
	
	public void updateUnit(boolean server, Unit unit, Unit newProperties)
	{
		if (server == true)
		{
			String command = "updateUnit\t" + unit + "\t" + newProperties;
			this.sendToAll(command);
		}
		super.updateUnit(server, unit, newProperties);
	}
	
	public void placeBuilding(boolean server, Building building) 
	{
		if (server == true)
		{
			String command = "placeBuilding\t" + building;
			this.sendToAll(command);
		}
		super.placeBuilding(server, building);
	}
	
	public void removeBuilding(boolean server, Building building) 
	{
		if (server == true)
		{
			String command = "removeBuilding\t" + building.getX() + "\t" + building.getY();
			this.sendToAll(command);
		}
		
		super.removeBuilding(server, building);
	}
	
	public void updateBuilding(boolean server, Building building, Building newProperties) 
	{
		if (server == true)
		{
			String command = "updateBuilding\t" + building + "\t" + newProperties;
			this.sendToAll(command);
		}
		
		super.updateBuilding(server, building, newProperties);
	}
	
	public void playerLost(boolean server,Player player) 
	{
		if (server == true)
		{
			String command = "playerLost\t" + player;
			this.sendToAll(command);
		}
		super.playerLost(server, player);
	}
	
	public void endTurn(boolean server)
	{	
		if (server == true)
		{
			String command = "endTurn";
			this.sendToAll(command);
		}
		
		super.endTurn(server);
	}
	
	public void gameOver(boolean server)
	{
		if (server == true)
		{
			String command = "gameOver";
			this.sendToAll(command);
		}
		
		super.gameOver(server);
	}
	
	/**
	 * Pump commands from clients to synchronize game
	 * @throws IOException 
	 */
	public void pumpCommands() throws IOException {
		int player = 0;
		for(BufferedReader r : this.cmdIStreams) {
			while(r.ready()) {
				String command = r.readLine();
				System.out.println("#" + player + ": " + command);
				this.parseCommand(command);
			}
			player++;
		}
	}
	
	public static GameServer spawnServer(ImportGame game, int nbPlayers, int port) throws IOException {
		
		ServerSocket socketserver = new ServerSocket(port);

		ArrayList< Player > players = new ArrayList< Player >();
		ArrayList< Socket > clients = new ArrayList< Socket >();
		
		for(int i = 0; i < nbPlayers; i++) {
			Socket client = socketserver.accept();
			players.add(new Player(i, "Player #"+i));
			clients.add(client);
		}
		
		GameServer gameServer = new GameServer(null, null, null, clients);
		
		game.read(gameServer);/*
		int i = 0;
		for(Player p : gameServer.getPlayers()) {
			p.setName(players.get(i).getName());
		}*/
		
		gameServer.sendToAll("transfertGameFile\n" + gameServer.toString());
		
		return gameServer;
	}
	
	/**
	 * Fonctions test du serveur.
	 * Les deux premières constantes spécifient le niveau à charger et le nombre de clients (entre 1 et l'infini).
	 * 
	 * Le serveur ne commencera pas à transmettre de données tant que le nombre requis de clients connectés
	 * n'est pas atteint.
	 * 
	 * @param a
	 * @throws UnknownHostException
	 * @throws IOException
	 */
	public static void main (String [] a) throws UnknownHostException, IOException
	{
		//
		/// Constantes de tests
		//
		
		//final String map = "data/levels/level0.tsv";
		final String map = "data/levels/level1.tsv";
		final int nbClients = 2;
		
		
		
		ImportGame game = new ImportGame(new BufferedReader(new InputStreamReader(new FileInputStream(map))));

		GameServer server = GameServer.spawnServer(game, nbClients, 34123);

		
			// Test placeUnit
			int i = 0;
			while(i < 10) {
				server.pumpCommands();
				server.placeUnit(true, new Unit(server.getUnitTypes().get("unit_infantry"), server.getPlayers().get(i%2), i, 5));
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				i++;
			}
			
			// Test removeUnit
			for(Unit u = server.getMap().getUnits().iterator().next(); server.getMap().getUnits().size() > 1; u = server.getMap().getUnits().iterator().next()) {
				server.pumpCommands();
				server.removeUnit(true, u);
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			// Test placeBuilding
			i = 0;
			while(i < 10) {
				server.pumpCommands();
				server.placeBuilding(true, new Building(server.getBuildingTypes().get("city"), server.getPlayers().get(i%2), i, 8));
				try {
					Thread.sleep(20);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				i++;
			}
			
			// Test removeBuilding
			for(Building b = server.getMap().getBuildings().iterator().next(); server.getMap().getBuildings().size() > 1; b = server.getMap().getBuildings().iterator().next()) {
				server.pumpCommands();
				server.removeBuilding(true, b);
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			// Test setTilemap
			i = 1;
			while(i != 0) {
				server.pumpCommands();
				server.setTile(true, i % server.getMap().getWidth(), i / server.getMap().getWidth(), 0);
				try {
					Thread.sleep(20);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				i = (i+1) % (server.getMap().getWidth() * server.getMap().getHeight());
			}


		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
